﻿#region 命名空间

using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Data.Entity.Core.Objects;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using Sharp.Domain.Model;

#endregion

namespace Sharp.Domain.Repository.EF
{
    /// <summary>
    /// IQueryable扩展
    /// </summary>
    public static partial class IQueryableExtensions
    {
        /// <summary>
        /// 过滤已经被逻辑删除的数据
        /// </summary>
        /// <param name="query"></param>
        /// <returns></returns>
        public static IQueryable<TAggregateRoot> Filter<TAggregateRoot>(this DbSet<TAggregateRoot> query)
            where TAggregateRoot : class
        {
            return Filter(query.AsQueryable());
        }
        /// <summary>
        /// 过滤已经被逻辑删除的数据
        /// </summary>
        /// <typeparam name="TAggregateRoot"></typeparam>
        /// <param name="query"></param>
        /// <returns></returns>
        public static IQueryable<TAggregateRoot> Filter<TAggregateRoot>(this IQueryable<TAggregateRoot> query)
            where TAggregateRoot : class
        {
            if (typeof(TAggregateRoot).GetInterface(nameof(IStandardState)) != null)
            {
                var left = Expression.Parameter(typeof(TAggregateRoot), "m");

                Expression expression = Expression.Constant(true);
                Expression right = Expression.Equal(Expression.Property(left, nameof(EntityState)),
                    Expression.Constant(EntityStateOperate.Normal));

                expression = Expression.And(right, expression);

                var filter = Expression.Lambda<Func<TAggregateRoot, bool>>(expression, left);

                return query.Where(filter);
            }
            return query;
        }

        #region OrderBy
        /// <summary>
        /// OrderBy排序
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="source"></param>
        /// <param name="ordering"></param>
        /// <param name="values"></param>
        /// <returns></returns>
        public static IQueryable<T> OrderBy<T>(this IQueryable<T> source, string ordering, params object[] values)
        {
            if (source == null) throw new ArgumentNullException(nameof(source));
            if (ordering == null) throw new ArgumentNullException(nameof(ordering));
            ParameterExpression[] parameters =
            {
                Expression.Parameter(source.ElementType, "")
            };

            var orderings = ParseOrdering(parameters, ordering, source.ElementType);

            Expression queryExpr = source.Expression;
            string methodAsc = "OrderBy";
            string methodDesc = "OrderByDescending";
            foreach (var o in orderings)
            {
                queryExpr = Expression.Call(
                    typeof(Queryable), o.Ascending ? methodAsc : methodDesc,
                    new[] { source.ElementType, o.Selector.Type },
                    queryExpr, Expression.Quote(Expression.Lambda(o.Selector, parameters)));
                methodAsc = "ThenBy";
                methodDesc = "ThenByDescending";
            }
            return (IQueryable<T>)source.Provider.CreateQuery<T>(queryExpr);
        }

        private static IEnumerable<DynamicOrdering> ParseOrdering(ParameterExpression[] parameters,
            string ordering, Type instanceType)
        {
            var orderings = new List<DynamicOrdering>();
            foreach (var o in ordering.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
            {
                var expr = ParseExpression(instanceType, parameters[0], o.Split(' ')[0]);
                bool ascending = o.IndexOf(" asc", StringComparison.CurrentCultureIgnoreCase) > -1;
                orderings.Add(new DynamicOrdering
                {
                    Selector = expr,
                    Ascending = ascending
                });

            }

            return orderings;
        }

        private static Expression ParseExpression(Type type, Expression instance, string field)
        {
            MemberInfo member = FindPropertyOrField(type, field, instance == null);
            if (member == null)
                throw new Exception("找不到排序的属性。");
            return member is PropertyInfo
                ? Expression.Property(instance, (PropertyInfo)member)
                : Expression.Field(instance, (FieldInfo)member);
        }

        private static MemberInfo FindPropertyOrField(Type type, string memberName, bool staticAccess)
        {
            BindingFlags flags = BindingFlags.Public | BindingFlags.DeclaredOnly |
                                 (staticAccess ? BindingFlags.Static : BindingFlags.Instance);
            foreach (Type t in SelfAndBaseTypes(type))
            {
                MemberInfo[] members = t.FindMembers(MemberTypes.Property | MemberTypes.Field,
                    flags, Type.FilterNameIgnoreCase, memberName);
                if (members.Length != 0) return members[0];
            }
            return null;
        }
        private static IEnumerable<Type> SelfAndBaseTypes(Type type)
        {
            if (type.IsInterface)
            {
                List<Type> types = new List<Type>();
                AddInterface(types, type);
                return types;
            }
            return SelfAndBaseClasses(type);
        }
        private static void AddInterface(IList<Type> types, Type type)
        {
            if (!types.Contains(type))
            {
                types.Add(type);
                foreach (Type t in type.GetInterfaces()) AddInterface(types, t);
            }
        }
        private static IEnumerable<Type> SelfAndBaseClasses(Type type)
        {
            while (type != null)
            {
                yield return type;
                type = type.BaseType;
            }
        }
        /// <summary>
        /// 排序载体
        /// </summary>
        public class DynamicOrdering
        {
            /// <summary>
            /// 升序
            /// </summary>
            public bool Ascending;
            /// <summary>
            /// 查找器
            /// </summary>
            public Expression Selector;
        }

        #endregion
    }
}